package DFA;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import ast_generator.ASTTree;
import ast_generator.TreeGenerator;

import nfa_generator.NFAGenerator;
import nfa_generator.NFAGraph;
import nfa_generator.NFANode;

public class DFA implements DFAInterface {
	
	private final DFAStateInterface startState;
	private final List<DFAStateInterface> dfaStates;
	private final Map<Integer, DFAStateInterface> readingKeyToDFAState;
	
	public DFA (NFAGraph nfa) {
		if (nfa == null)
			throw new IllegalArgumentException("nfa argument must not be null");
		Set<NFANode> epsilonClosure = nfa.getStartState().epsilonTraverse();
		this.startState = DFAStateFactory.generate(epsilonClosure);
		this.dfaStates = new ArrayList<DFAStateInterface>();
		generateDFA(startState);
		readingKeyToDFAState = new HashMap<Integer, DFAStateInterface>();
	}
	
	private void generateDFA (DFAStateInterface curDFAState) {
		if (!dfaStates.contains(curDFAState)) {
			dfaStates.add(curDFAState);
			((DFAState)curDFAState).buildTransitions();
			for (DFAStateInterface dfaState : ( (DFAState)curDFAState).getNeighbors() ) 
				generateDFA(dfaState);
		}
	}
	
	@Override
	public int generateReadingKey() {
		int readingKey = (int)	(Math.random() * Integer.MAX_VALUE +
								 Math.random() * Integer.MIN_VALUE) /2;
		readingKeyToDFAState.put(readingKey, startState);
		return readingKey;
	}
	
	@Override
	public String readNext (int readingKey, String input) throws DFAException {
		DFAStateInterface curDFAState = readingKeyToDFAState.get(readingKey);
		if (curDFAState == null)
			throw new IllegalArgumentException("The reading key does not exist");
		if (input == null)
			throw new IllegalArgumentException("input must not be null");
		
		String output = null;
		DFAStateInterface nxtDFAState = curDFAState.nextDFAState(input);
		if (nxtDFAState == null) {
			output = curDFAState.getToken();
			if (output == null)
				throw new DFAException("Nested or non-existed tokens are not allowed");
		} else
			readingKeyToDFAState.put(readingKey, nxtDFAState);
		return output;
	}
	
	@Override
	public String read (int readingKey, String input) {
		DFAStateInterface curDFAState = readingKeyToDFAState.get(readingKey);
		if (curDFAState == null)
			throw new IllegalArgumentException("The reading key does not exist");
		if (input == null)
			throw new IllegalArgumentException("input must not be null");
		
		String output = null;
		DFAStateInterface nxtDFAState = curDFAState.nextDFAState(input);
		if (nxtDFAState != null) {
			if (nxtDFAState.getToken() == null)
				output = "";
			else
				output = nxtDFAState.getToken();
			readingKeyToDFAState.put(readingKey, nxtDFAState);
		}
		return output;
	}
	
	@Override
	public void resetReading(int readingKey) {
		readingKeyToDFAState.put(readingKey, startState);
	}
	
	public static void main (String[] args) {
		ArrayList<String> idAndRegex = new ArrayList<String>();
		idAndRegex.add("$ACEPTING");
		idAndRegex.add("[a-zA-z]");
		List<ASTTree> astTreeList = TreeGenerator.makeTreeFromString(idAndRegex);
		NFAGenerator nfaGenerator = new NFAGenerator(astTreeList);
		NFAGraph nfaGraph = nfaGenerator.generateNFA();
		DFA dfa = new DFA(nfaGraph);
		int readingkey = dfa.generateReadingKey();
		
		for (char chr : "A a A a B b".toCharArray()) {
			System.out.print( dfa.read(readingkey, "" + chr) + ", " );
		}
	}
}
